#include "../include/DictProducer.h"
#include "../include/SplitToolCppJieba.h" // 包含分词工具的头文件

#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <unordered_set>

using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using std::istringstream;
using std::map;
using std::set;
using std::string;
using std::vector;
using std::ofstream;

// 添加构造函数和析构函数的定义
DictProducer::DictProducer(SplitTool* cuttor)
    : _cuttor(cuttor)  // 初始化分词工具指针
{
}

DictProducer::~DictProducer()
{
}



void DictProducer::dealLine(string &line)
{
    for (auto &ch : line) // ch是字符串(std::string&类型)的每一个字符，auto是char类型（准确来说是char&）
    {
        // 如果该字符是是符号或数字就替换成空格
        if (!isalpha(ch))
        {
            ch = ' ';
        }
        else if (isupper(ch))
        { // 说明是字母，则考虑大小写问题（比如同一个单词出现在句首，出现在句中。实际上都是一个单词）
            // 如果是大写字母，就把它改为小写字母
            ch = tolower(ch);
        }
    }
}



// 处理英文语料的函数
void DictProducer::dealEN(const string &path)
{
    ifstream ifs(path); // 读取文件内容
    if (!ifs)
    {
        cerr << "ifstream open English_file failed" << endl;
        return;
    }

    // 接下来关于读取的核心2个步骤：
    // 1-std::getline(ifs, line) 从文件输入流 ifs 中逐行读取内容，读取的数据存储在 line 这个字符串变量中。
    // 2-istringstream iss(line) 将 line 这个字符串放入流中，接着使用 iss >> word 从字符串流中逐个提取单词。

    // 读取第二步：按行处理读取到的文件内容
    string line;           // 字符串，用来存储文件每一行内容
    size_t lineNumber = 1; // 行号，从1开始。因此需要注意与下标（从0开始）的关系
    while (std::getline(ifs, line))
    {

        dealLine(line); // 处理每一行内容

        // 读取第三步：istringstream(iss)用于从字符串中提取数据,它的数据源是一个字符串
        istringstream iss(line); // 创建一个iss对象，用line字符串初始化
        string word;
        while (iss >> word) // 从iss流中提取下一个以空格或其他空白符为分隔的单词，并将其存储到 `word` 中。
        {
            // 统计单词和其出现的次数
            ++_dictEN[word];
        }

        ++lineNumber; // 处理完一行，继续下一行
    }

    ifs.close(); // 关闭文件

    //参考中文map->vector
    //再通过vector 构建 map 
    for(auto & elem:_dictEN){
        _vecEN.push_back(make_pair(elem.first,elem.second));
    }

    //构建索引
    for(size_t i = 0; i < _vecEN.size(); ++i){
        const string& word = _vecEN[i].first;  // 获取单词
        string tmp;
        
        // 遍历单词中的每个字母
        for(size_t j = 0; j < word.length(); ++j) {
            // 将字母转为小写并构造成string
            tmp = string(1, tolower(word[j]));
            // 将该字母对应的单词下标加入到集合中
            _indexEN[tmp].insert(i);
        }
    }

    storeEN(EN_DICT_PATH);
    storeIndexEN(EN_INDEX_PATH);
}















//处理中文语料的函数
// 添加一个函数来加载停用词
void DictProducer::loadStopWords(const std::string &filePath) {
    std::ifstream ifs(filePath);
    if (!ifs) {
        cerr << "ifstream open stop_words file failed" << endl;
        return;
    }
    std::string word;
    while (std::getline(ifs, word)) {
        _stopWords.insert(word);
    }
    ifs.close();
}

//构成中文词典
void DictProducer::dealCN(const string &path) {
    ifstream ifs(path);
    if (!ifs) {
        cerr << "ifstream open Chinese_file failed" << endl;
        return;
    }

    // 一次性读取整个文件内容
    string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    ifs.close();

    // 先处理文本内容，去除非中文字符
    dealLine_Chinese(content);

    /*
    // 使用Jieba进行分词
    SplitToolCppJieba splitter;
    vector<string> words = splitter.cut(content);
    */

    // 使用基类指针调用 cut 方法
    vector<string> words = _cuttor->cut(content);

    // 统计词频
    for (const auto &word : words) {
        if (_stopWords.find(word) == _stopWords.end()) {
            ++_dictCN[word];
        }
    }

    //在这里已经词频统计完成，此时的容器是map<string,int>
    //接下来需要将map<string,int>转为vector<pair<string,int>>
    for(auto &elem:_dictCN){
        _vecCN.push_back(make_pair(elem.first,elem.second));
    }



    //此时，map容器转化成了vector容器
    //通过vector的string,可以知道它的下标。即可以使用下标表示string
    //接下来，使用 要查找的单词（key） 和 下标(value，严格来说是下标集合)构成一个map容器
    // 构建索引：遍历vector中的每个词，对每个词中的每个汉字建立索引
    for(size_t i = 0; i < _vecCN.size(); ++i) 
    {
        const string& word = _vecCN[i].first;  // 获取词
        string tmp;
        
        // 遍历词中的每个字符
        for(size_t j = 0; j < word.length();) {
            if((unsigned char)word[j] >= 0xE0) {  // UTF-8编码的中文字符（3字节）
                // 提取一个完整的UTF-8中文字符
                tmp = word.substr(j, 3);
                // 将该字对应的词的下标加入到集合中
                _indexCN[tmp].insert(i);
                j += 3;  // 移动到下一个中文字符
            } else {
                j++;    // 跳过非中文字符
            }
        }
    }

    storeCN(CN_DICT_PATH);
    storeIndexCN(CN_INDEX_PATH);
}


//处理每一行中文语料都为中文
std::string DictProducer::dealLine_Chinese(std::string &line) {
    string result;
    for (size_t i = 0; i < line.length();) {
        unsigned char ch = line[i];
        if ((ch & 0xE0) == 0xE0) {  // UTF-8编码的中文字符（3字节）
            // 检查是否还有足够的字节
            if (i + 2 < line.length()) {
                // 复制这个完整的UTF-8字符（3个字节）
                result.append(line.substr(i, 3));
            }
            i += 3;
        } else {
            // 非中文字符替换为空格
            result.append(" ");
            i++;
        }
    }
    line = result;
    return line;
}

















//存储vec容器——中文，英文
// 存储英文词典_vec容器
void DictProducer::storeEN(const string& filepath) {
    std::ofstream ofs(filepath);
    if (!ofs) {
        cerr << "ofstream open English dictionary file failed" << endl;
        return;
    }

    // 写入英文词典大小
    ofs << _vecEN.size() << endl;

    // 写入英文词典内容
    for (const auto& pair : _vecEN) {
        ofs << pair.first << " " << pair.second << endl;
    }

    ofs.close();
}

// 存储中文词典_vec容器
void DictProducer::storeCN(const string& filepath) {
    std::ofstream ofs(filepath);
    if (!ofs) {
        cerr << "ofstream open Chinese dictionary file failed" << endl;
        return;
    }

    // 写入中文词典大小
    ofs << _vecCN.size() << endl;

    // 写入中文词典内容
    for (const auto& pair : _vecCN) {
        ofs << pair.first << " " << pair.second << endl;
    }

    ofs.close();
}


//存储map容器——中文，英文
// 存储英文索引
void DictProducer::storeIndexEN(const string& filepath) {
    std::ofstream ofs(filepath);
    if (!ofs) {
        cerr << "ofstream open English index file failed" << endl;
        return;
    }

    // 写入索引大小
    ofs << _indexEN.size() << endl;

    // 写入索引内容
    for (const auto& index : _indexEN) {
        // 写入key（字母）
        ofs << index.first << " ";
        // 写入该字母对应的单词下标集合大小
        ofs << index.second.size() << " ";
        // 写入所有下标
        for (const auto& idx : index.second) {
            ofs << idx << " ";
        }
        ofs << endl;
    }

    ofs.close();
}

// 存储中文索引
void DictProducer::storeIndexCN(const string& filepath) {
    std::ofstream ofs(filepath);
    if (!ofs) {
        cerr << "ofstream open Chinese index file failed" << endl;
        return;
    }

    // 写入索引大小
    ofs << _indexCN.size() << endl;

    // 写入索引内容
    for (const auto& index : _indexCN) {
        // 写入key（汉字）
        ofs << index.first << " ";
        // 写入该汉字对应的词语下标集合大小
        ofs << index.second.size() << " ";
        // 写入所有下标
        for (const auto& idx : index.second) {
            ofs << idx << " ";
        }
        ofs << endl;
    }

    ofs.close();
}




